package com.boby.homecamera.helper

import com.boby.homecamera.config.AppConfig
import com.boby.homecamera.data.WebSocketData
import com.boby.homecamera.db.DBManager
import com.boby.homecamera.db.VideoEventTable.VideoEvent
import com.boby.homecamera.service.MainService
import com.boby.homecamera.util.FileUtil
import com.boby.lib_common.BaseApplication
import com.boby.lib_common.util.BLog
import com.boby.lib_common.util.ByteUtil
import com.k2fsa.sherpa.onnx.AudioEvent
import com.k2fsa.sherpa.onnx.audio.tagging.Tagger
import java.util.concurrent.LinkedBlockingDeque

class AudioDetectHelper {
    private val TAG = "AudioDetectHelper"
    private val sampleBlockingDeque = LinkedBlockingDeque<Pair<Long, ByteArray>>()
    private var isStart = false
    private var threshold = 0.6F
    private val sampleList = ArrayList<FloatArray>()
    private var maxComputeTime = 10_000 // 10s
    private var thread: Thread? = null
    private var startTime = 0L
    fun start() {
        thread = Thread {
            while (isStart) {
                try {
                    val item = sampleBlockingDeque.take()
                    val bytes = item.second
                    if (bytes.isNotEmpty()) {
                        if (startTime == 0L) {
                            startTime = item.first
                        }
                        val samples = getFloatArray(bytes,bytes.size)
                        sampleList.add(samples)
                    }

                    if (item.first - startTime > maxComputeTime) {
                        val samples = Flatten(sampleList)
                        val stream = Tagger.tagger.createStream()
                        stream.acceptWaveform(samples, AppConfig.Record.RECORD_AUDIO_SAMPLE_RATE)
                        val events = Tagger.tagger.compute(stream)
                        stream.release()
                        result(events, startTime, item.first)
                        sampleList.clear()
                        startTime = 0L
                    }

                } catch (e: Exception) {
                    e.printStackTrace()
                }

            }

        }
        isStart = true
        thread?.start()
    }

    private fun getFloatArray(pcmByteArray: ByteArray,size:Int):FloatArray{
        val floatArray = FloatArray(size/2)

        for (i in 0 until size step 2) {
            floatArray[i / 2] = ByteUtil.byte2Short (pcmByteArray[i ],pcmByteArray[i + 1]).toFloat() / 32768.0f
//            Log.d(TAG,"getFloatArray ${i / 2} ${floatArray[i / 2]}")
        }
//        Log.d(TAG,"getFloatArray $size ")
        return floatArray
    }

    private fun result(events: ArrayList<AudioEvent>, startTime: Long, endTime: Long) {
        //检测结果 1.说话， 2.婴儿哭泣
        val eventsSorted = events.sortedByDescending { it.prob }
        var speechAudioEvent:AudioEvent? = null
        for (e in eventsSorted) {
            BLog.d(TAG, "result: ${e.name},${e.index}, ${e.prob} ")
            if (e.prob > threshold) {

                if (Tagger.isCry(e.index)) {
                    saveVideoEvent(VideoEvent(eventType = 2, startTime = startTime, endTime = endTime, threshold = e.prob, des = "${e.index}"))
                    return
                } else if (speechAudioEvent == null && Tagger.isSpeech(e.index)) {
                    speechAudioEvent = e
                }
            }

        }
        if(speechAudioEvent != null ){
            saveVideoEvent(VideoEvent(eventType = 1, startTime = startTime, endTime = endTime, threshold = speechAudioEvent.prob,des = "${speechAudioEvent.index}"))
        }


    }

    private var lastVideoEvent: VideoEvent? = null
    private fun saveVideoEvent(event: VideoEvent) {
        if (lastVideoEvent == null) {
            var id = DBManager.instance.tb_videoEvent.insertVideoEvent(event)
            val photoPath = "${com.boby.homecamera.constant.Constant.RECORD_FILE_DIR}${System.currentTimeMillis()}_msg${event.eventType}.jpeg"
            event.id = id
            event.capturePhoto = photoPath
            savePhoto(photoPath)
            lastVideoEvent = event
            MainService.webSocketServer?.sendVideoEvent(event)
        }  else if (lastVideoEvent!!.eventType == event.eventType  && event.startTime - lastVideoEvent!!.endTime < 5000) {
            lastVideoEvent!!.endTime = event.endTime
            lastVideoEvent!!.des = "${event.des}"
            DBManager.instance.tb_videoEvent.updateVideo(lastVideoEvent!!)
//        } else if (lastVideoEvent!!.eventType != event.eventType  && event.startTime - lastVideoEvent!!.endTime > 5000)){
//        } else if (lastVideoEvent!!.eventType != event.eventType  && (event.startTime - lastVideoEvent!!.endTime > 5000 || event.eventType == 2)){//婴儿哭泣的话，可以直接转
        } else {

            val photoPath = "${com.boby.homecamera.constant.Constant.RECORD_FILE_DIR}${System.currentTimeMillis()}_msg${event.eventType}.jpeg"
            event.capturePhoto = photoPath
            savePhoto(photoPath)

            val id = DBManager.instance.tb_videoEvent.insertVideoEvent(event)
            event.id = id
            lastVideoEvent = event

            MainService.webSocketServer?.sendVideoEvent(event)
        }


    }


    fun onPcm(bytes: ByteArray) {
        try {
            if (isStart) {
                sampleBlockingDeque.put(Pair(System.currentTimeMillis(), bytes))
            }
        } catch (e: Exception) {
            e.printStackTrace()
        }


    }

    fun release() {
        isStart = false

        try {
            sampleBlockingDeque.clear()
            thread?.interrupt()
            thread = null
        } catch (e: Exception) {
            e.printStackTrace()
        }

    }


    private fun Flatten(sampleList: ArrayList<FloatArray>): FloatArray {
        var totalSamples = 0
        for (a in sampleList) {
            totalSamples += a.size
        }
        var i = 0
        val samples = FloatArray(totalSamples)
        for (a in sampleList) {
            for (s in a) {
                samples[i] = s
                i += 1
            }
        }
//        Log.i(TAG, "$i, $totalSamples")

        return samples
    }
    private fun savePhoto(path:String){
        MainService.recordServer?.glInterface?.takePhoto(AppConfig.Record.CAPTURE_WIDTH/2,AppConfig.Record.CAPTURE_HEIGHT/2){
            try {
                BaseApplication.getWorkHandler()?.post {
                    FileUtil.saveBitmapToDir(it,null,path)
                }
            }catch (e:Exception){
                e.printStackTrace()
            }

        }
    }

}